#pragma once

#include <iostream>
#include <string>
#include <unordered_map>
#include <fstream>

#include <cstring>

#include <unistd.h>

#include "log.hpp"
extern Log log;

#define RECV_SIZE 1024
void Service(int sockfd, const std::string& clientip, uint16_t clientport)
{
    char inbuffer[RECV_SIZE];
    while (true)
    {
        ssize_t n = read(sockfd, inbuffer, RECV_SIZE - 1);
        if (n > 0)
        {
            inbuffer[n] = 0;
            std::cout << "[" << clientip << ":" << clientport << "]# " << inbuffer << std::endl;
            std::string echo_string = "tcpserver echo# ";
            echo_string += inbuffer;
            write(sockfd, echo_string.c_str(), echo_string.size());
        }
        else if (n == 0)
        {
            log(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
            return;
        }
        else
        {
            log(Warning, "read error, sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip.c_str(), clientport);
            return;
        }
    }
}
#undef RECV_SIZE

class Translate
{
    const std::string dictname = "./dict.txt";
public:
    enum Translate_Error
    {
        IFSTREAM_OPEN_ERROR = 1,
        IFSTREAM_READ_ERROR
    };

    Translate()
    {
        std::ifstream in(dictname);
        if (!in.is_open())
        {
            log(Fatal, "ifstream open %s error, errno: %d, errstring: %s", dictname.c_str(), errno, strerror(errno));
            exit(IFSTREAM_OPEN_ERROR);
        }
        std::string line;
        while (std::getline(in, line))
        {
            std::string part1, part2;
            if (!split(line, part1, part2))
            {
                log(Fatal, "ifstream read %s error, errno: %d, errstring: %s", dictname.c_str(), errno, strerror(errno));
                exit(IFSTREAM_READ_ERROR);
            }
            _dict.insert({part1, part2});
        }
        in.close();
    }
    std::string translation(const std::string& key)
    {
        auto iter = _dict.find(key);
        if (iter == _dict.end())
            return "unkonw";
        return iter->second;
    }
private:
    bool split(const std::string& line, std::string& part1, std::string& part2)
    {
        size_t pos = line.find(':');
        if (pos == std::string::npos)
            return false;
        part1 = line.substr(0, pos);
        part2 = line.substr(pos + 1);
        return true;
    }
    std::unordered_map<std::string, std::string> _dict;
}ts;

template<size_t RECV_SIZE = 4096>
class Task
{
public:
    Task(int sockfd, const std::string& clientip, uint16_t clientport)
        : _sockfd(sockfd)
        , _clientip(clientip)
        , _clientport(clientport)
    {}
    void operator()()
    {
        while (true)
        {
            char buffer[RECV_SIZE];
            ssize_t n = read(_sockfd, buffer, sizeof(buffer));
            if (n > 0)
            {
                buffer[n] = 0;
                std::cout << "client key# " << buffer << std::endl;
                std::string echo_string = ts.translation(buffer);
                n = write(_sockfd, echo_string.c_str(), echo_string.size());
                if (n < 0)
                {
                    log(Warning, "write error, sockfd: %d, clientip: %s, clientport: %d, errno: %d, errstring: %s", _sockfd, _clientip.c_str(), _clientport, errno, strerror(errno));
                    break;
                }
            }
            else if (n == 0)
            {
                log(Info, "%s:%d quit, server close sockfd: %d", _clientip.c_str(), _clientport, _sockfd);
                break;
            }
            else
            {
                log(Warning, "read error, sockfd: %d, clientip: %s, clientport: %d, errno: %d, errstring: %s", _sockfd, _clientip.c_str(), _clientport, errno, strerror(errno));
                break;
            }
        }
        close(_sockfd);
    }
private:
    int _sockfd;
    std::string _clientip;
    uint16_t _clientport;
};